#include <opencv2/videoio.hpp>
#include <opencv2/videoio/videoio_c.h>
#include <opencv2/core.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/imgproc.hpp>

#include <iostream>
#include <string>
#include <sstream>
#include <filesystem>

#ifdef WIN32
# define _SILENCE_EXPERIMENTAL_FILESYSTEM_DEPRECATION_WARNING
#include <experimental/filesystem>
namespace fs = std::experimental::filesystem::v1;

#else   // UNIX-like
namespace fs = std::filesystem;
#endif

using cv::VideoCapture;

int capvideo(const std::string& name) {
    fs::path img_path = fs::current_path();
    img_path.append("pic");

    if (!fs::exists("pic")) {
        fs::create_directories("pic");
    }

    VideoCapture capture(name);
    int fps = capture.get(CV_CAP_PROP_FPS);
    if (!capture.isOpened()) {
        std::cerr << "Unable to open file\n";
        abort();
    }

    cv::Mat frame;
    for (int i = 0; true; i++) {
        std::stringstream ibuff;
        ibuff << img_path.u8string() << "/test_" << i << ".png";

        capture.read(frame);

        if (frame.empty()) {
            std::cout << "blank frame, grab done.\n";
            break;
        }

        cv::imwrite(ibuff.str(), frame);
    }

    return fps;
}

void regen_video(int fps) {
    if (!fs::exists("new")) {
        std::cerr << "not found new folder\n";
        abort();
    }

    fs::path img_path = fs::current_path();
    img_path.append("new");
    std::stringstream buff;
    buff << img_path.u8string() << "/test_0.png";
    cv::Mat img = cv::imread(buff.str(), cv::IMREAD_COLOR);

    cv::VideoWriter vwriter("result.mpeg", cv::VideoWriter::fourcc('P', 'I', 'M', '1'), fps, img.size());

    uint64_t count = 0;
    for (auto iter : fs::directory_iterator("new")) {
        std::stringstream sbuff;
        sbuff << img_path.u8string() << "/test_" << count << ".png";

        cv::Mat iobj = cv::imread(sbuff.str(), cv::IMREAD_COLOR);
        vwriter.write(iobj);
        count++;
    }
}

cv::Mat draw_char(const cv::Mat &img, const std::string &str) {
    cv::Mat result(img.rows, img.cols, CV_8UC3, cv::Scalar(255, 255, 255));
    int r = 0, c = 8;
    
    while (r < img.rows) {
        while (c < img.cols) {
            for (int i = 0; i < str.length(); i++) {
                if (c >= img.cols) {
                    break;
                }

                uint8_t red = img.at<cv::Vec3b>(r, c)[2];
                uint8_t green = img.at<cv::Vec3b>(r, c)[1];
                uint8_t blue = img.at<cv::Vec3b>(r, c)[0];

                cv::putText(result, str.substr(i, 1), cv::Point(c, r),
                            cv::FONT_HERSHEY_SIMPLEX, 0.35,
                            cv::Scalar(blue, green, red));
                c += 8;
            }
        }

        c = 8;
        r += 9;
    }
   
    return result;
}

void gen_img(const std::string &str) {
    if (!fs::exists("pic")) {
        std::cerr << "not found pic folder\n";
        abort();
    }
    if (!fs::exists("new")) {
        fs::create_directories("new");
    }
    fs::path newimg_path = fs::current_path();
    newimg_path.append("new");
    fs::path pic_path = fs::current_path();
    pic_path.append("pic");

    uint64_t count = 0;
    for (auto iter : fs::directory_iterator("pic")) {
        std::stringstream buff;
        std::stringstream pbuff;
        buff << newimg_path.u8string() << "/test_" << count << ".png";
        pbuff << pic_path.u8string() << "/test_" << count << ".png";
        

        cv::Mat imgobj = cv::imread(pbuff.str(), cv::IMREAD_COLOR);
        cv::Mat genimg = draw_char(imgobj, str);

        cv::imwrite(buff.str(), genimg);
        count++;
    }
}

int main(int argc, const char** argv) {
    if (argc < 2) {
        std::cerr << argv[0] << " [video filename]\n";
        return -1;
    }

    int fps = capvideo(argv[1]);
    gen_img("testfont");
    regen_video(fps);

    return 0;
}
